You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
70 lines
2.0 KiB
70 lines
2.0 KiB
<script setup lang="ts">
|
|
import { unwrapApiBody, type ApiResponse } from '../../../utils/http/factory'
|
|
|
|
definePageMeta({
|
|
layout: 'public',
|
|
})
|
|
|
|
const route = useRoute()
|
|
const publicSlug = computed(() => route.params.publicSlug as string)
|
|
const postSlug = computed(() => route.params.postSlug as string)
|
|
|
|
type Post = {
|
|
title: string
|
|
slug: string
|
|
excerpt: string
|
|
bodyMarkdown: string
|
|
coverUrl: string | null
|
|
publishedAt: Date | null
|
|
}
|
|
|
|
const { data, pending, error } = await useAsyncData(
|
|
() => `public-post-${publicSlug.value}-${postSlug.value}`,
|
|
async () => {
|
|
const path = `/api/public/profile/${encodeURIComponent(publicSlug.value)}/posts/${encodeURIComponent(postSlug.value)}`
|
|
const res = await $fetch<ApiResponse<{ post: Post }>>(path)
|
|
return unwrapApiBody(res).post
|
|
},
|
|
{ watch: [publicSlug, postSlug] },
|
|
)
|
|
|
|
watchEffect(() => {
|
|
if (data.value?.title) {
|
|
useHead({ title: data.value.title })
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<UContainer class="py-10 space-y-6">
|
|
<div v-if="pending" class="text-muted">
|
|
加载中…
|
|
</div>
|
|
<UAlert v-else-if="error" color="error" title="文章不存在或未公开" />
|
|
<template v-else-if="data">
|
|
<UButton :to="`/@${publicSlug}`" variant="ghost" color="neutral" size="sm" class="-ml-2">
|
|
← 返回主页
|
|
</UButton>
|
|
<div v-if="data.coverUrl" class="flex justify-center">
|
|
<img
|
|
:src="data.coverUrl"
|
|
alt=""
|
|
class="max-h-64 w-full max-w-2xl rounded-lg object-cover border border-default"
|
|
>
|
|
</div>
|
|
<p v-if="data.publishedAt" class="text-sm text-muted">
|
|
{{ data.publishedAt }}
|
|
</p>
|
|
<h1 class="text-2xl font-semibold">
|
|
{{ data.title }}
|
|
</h1>
|
|
<p v-if="data.excerpt" class="text-muted">
|
|
{{ data.excerpt }}
|
|
</p>
|
|
<article class="prose dark:prose-invert max-w-none whitespace-pre-wrap">
|
|
{{ data.bodyMarkdown }}
|
|
</article>
|
|
<PostComments mode="public-post" :public-slug="publicSlug" :post-slug="postSlug" />
|
|
</template>
|
|
</UContainer>
|
|
</template>
|
|
|